package genetics;

import java.util.Collection;
import java.util.Collections;
import java.util.Random;
import java.util.Vector;

public class Evolver<E extends GenEntity> {
	private Vector<GenEntity> population = new Vector<GenEntity>();
	private static Random rnd = null;
	
	public Evolver()
	{
		if(rnd == null) rnd = new Random();
	}
	
	public void setPopulation(Collection<E> pop)
	{
		population.clear();
		population.addAll(pop);
	}
	
	private void selection(float medianBias)
	{
		Vector<Integer> score = new Vector<Integer>();
		Vector<Integer> med = new Vector<Integer>();
		
		for(int i = 0; i < population.size(); ++i)
		{
			int fit = population.get(i).fitness();
			score.add(fit);
			med.add(fit);
		}
		
		Collections.sort(med);
		int median = med.get((int)(med.size() * medianBias));
		System.out.println("Median : " + median);
		
		Vector<GenEntity> nPop = new Vector<GenEntity>();
		for(int i = 0; i < population.size(); i++)
		{
			if(score.get(i) >= median)
				nPop.add(population.get(i));
		}
		
		population = nPop;
	}
	
	private void reproduce()
	{
		Collections.shuffle(population);
		int size = population.size();
		
		if(size < 2) return;
		
		//Vector<GenEntity> nPop = new Vector<GenEntity>();
		
		for(int i = 0; i < size - 1; i += 2)
		{
			GenEntity[] children = population.get(i).crossOver(population.get(i + 1));
			for(GenEntity child : children)
				population.add(child);
				//nPop.add(child);
		}
		
		//population = nPop;
	}
	
	private void mutate(int percent)
	{
		int num = (int) ((float)percent / 100.0f * (float)population.size());
		for(int i = 0; i < num; ++i)
		{
			//int index = rnd.nextInt(population.size());
			population.get(i).mutate();
		}
	}
	
	@SuppressWarnings("unchecked")
	public Vector<E> doEvolution(int maxGenerations, int mutationPercent, float medianBias)
	{
		for(int i = 0; i < maxGenerations; ++i)
		{
			System.out.println("Generation " + i);
			if(population.size() <= 1) break;
			selection(medianBias);
			System.out.println("Selection done.");
			if(population.size() <= 1) break;
			reproduce();
			System.out.println("Reproduction done.");
			mutate(mutationPercent);
			System.out.println("Mutation done.");
			System.out.println("Count : " + population.size());
			
		}
		
		return (Vector<E>) population;
	}
	
	public static Random getRnd()
	{
		if(rnd == null) rnd = new Random();
		return rnd;
	}
}
